/**
 * @author luguoxiang
 * @Date: 2023/8/21 16:55
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
/**
 * 解决数学计算并非因为浮点数有效字数限制导致的精度丢失问题(因浮点数有效位数只有15位，有效数位是整数加小数的总位数)
 * 注意：本函数只能解决js导致的非位数超出15位的精度问题，如果计算结果本身就是超过十五位则一定会丢失精度，这是浮点数不能避免的问题
 * 并非因为浮点数有效字数限制导致的精度丢失问题举例：0.1+0.2=0.30000000000000004，这种问题通过本函数可以得到准确的结果，
 * 本身计算结果超出举例：9999990123456789/1000000000000000=9.999990123456788，这种本身超出15位的精度问题本函数无法解决，这是浮点数限制，无法跳出
 * @param value 数值
 */
export function toValidValue(value: number) {
  // @ts-ignore
  const str = String(value);
  const index = str.indexOf('.');
  if (index === -1) {
    // 不包含小数不处理（不处理不代表精确，因为超过15位有效字数一定有精度问题）
    return value;
  }
  if (str.length <= 16) {
    // 在有效数字位数范围内
    return value;
  }
  // 通过四舍五入获取精度范围内的有效数值
  let precision = 15 - index;
  precision = 10 ** precision;
  return Math.round(value * precision) / precision;
}

/**
 * 表达式运算
 * @param expression 表达式
 */
export function calc(expression) {
  const func = Function;
  // eslint-disable-next-line new-cap
  return new func(`return ${expression}`)();
}

/**
 * 加法（为null的项按0处理）
 * @param args
 */
export function add(...args) {
  let result = 0;
  for (const arg of args) {
    result += Number(arg ?? 0);
  }
  return result;
}

/**
 * 减法
 * @param args
 */
export function sub(...args) {
  let result = 0;
  for (const arg of args) {
    result -= Number(arg ?? 0);
  }
  return result;
}

/**
 * 乘法（任意一项为null结果为null）
 * @param args
 */
export function mul(...args) {
  let result = 1;
  for (const arg of args) {
    if (isNotNumber(arg)) {
      return null;
    }
    result *= Number(arg);
  }
  return result;
}

/**
 * 除法（任意一项为null结果为null）
 * @param divisor
 * @param args
 */
export function div(divisor: any, ...args) {
  if (isNotNumber(divisor) || args.length === 0) {
    return null;
  }
  let result = Number(divisor);
  for (const arg of args) {
    if (isNotNumber(arg)) {
      return null;
    }
    result /= Number(arg);
  }
  return result;
}
/**
 * 精确到小数点后x位（注意因js原因整数加小数的有效数字只有15位）
 * @param num 数值
 * @param precision 精度
 */
export function round(num: number, precision = 10) {
  if (!num) {
    return num;
  }
  const numStr = `${num}`;
  const index = numStr.indexOf('e');
  if (index !== -1) {
    const n1 = numStr.substring(0, index);
    const n2 = +numStr.substring(index + 1) + precision;
    if (n2 > 0) {
      return +`${Math.round(+`${n1}e+${n2}`)}e-${precision}`;
    }
    return +`${Math.round(+`${n1}e${n2}`)}e-${precision}`;
  }
  return +`${Math.round(+`${num}e+${precision}`)}e-${precision}`;
}

/**
 * 是否不是一个数字
 * @param v
 */
function isNotNumber(v: any) {
  // eslint-disable-next-line no-restricted-globals
  return v === null || v === '' || isNaN(v);
}
